#import "TVController.h"
#import <appkit/Application.h>
#import <appkit/publicWraps.h>
#import <appkit/NXEPSImageRep.h>
#import <appkit/NXBitmapImageRep.h>
#import <appkit/Control.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <streams/streams.h>
#import "ToyWin.h"
#import "ToyWinEPS.h"
#import "ToyView.h"
#import "save.h"
#import "common.h"

#define  Width45(x,y)	((((x)+1) >> 1) + (((y)+1) >> 1))
#define  NewLoc		13

@implementation TVController (Rotate)

static void packWorkingImage(const commonInfo *newinf, int pl,
	unsigned char **working, unsigned char **planes)
{
	int	pn, y;
	unsigned char *pp, *ww;

	for (pn = 0; pn < pl; pn++) {
	    for (y = 0; y < newinf->height; y++) {
		ww = working[pn] + y * newinf->width;
		pp = planes[pn] + y * newinf->xbytes;
		packImage(pp, ww, newinf->width, newinf->bits);
	    }
	}
}

/* Local Method */
- doBitmap: (const char *)fn info:(commonInfo *)cinf
		to:(int)angle rect:(NXRect *)select
{
	ToyWin	*tw;
	commonInfo *newinf = NULL;
	unsigned char *working[5], *planes[5];
	int	i, pidx, ptr, x, y, w, pl;
	int	pix[5], idx[5];

	working[0] = planes[0] = NULL;
	tw = NULL;
	if ((newinf = (commonInfo *)malloc(sizeof(commonInfo))) == NULL)
		goto ErrEXIT;
	*newinf = *cinf;
	if (cinf->cspace == NX_OneIsBlackColorSpace)
		newinf->cspace = NX_OneIsWhiteColorSpace;
		/* getPixel() fixes 0 as Black */
	newinf->isplanar = YES;
	if (angle == 90 || angle == 270) {
		newinf->width = cinf->height;
		newinf->height = cinf->width;
	}else if (angle == 45) {
		newinf->width = newinf->height
			= Width45(cinf->width, cinf->height);
	}else if (angle == Clip) {
		newinf->width = select->size.width;
		newinf->height = select->size.height;
	}
	newinf->xbytes = byte_length(newinf->bits, newinf->width);
	newinf->palette = NULL;
	if (cinf->palette) {
		w = sizeof(paltype) * newinf->palsteps;
		if ((newinf->palette = (paltype *)malloc(w)) == NULL)
			goto ErrEXIT;
		memcpy((void *)newinf->palette, (void *)cinf->palette, w);
	}

	pl = newinf->numcolors;
	for (i = 0; i < pl; i++) idx[i] = i;
	for (i = pl; i < 5; i++) idx[i] = -1;
	if (newinf->alpha) idx[ALPHA] = pl++;
	w = newinf->width * newinf->height;
	if ((working[0] = (unsigned char *)calloc(w * pl, 1)) == NULL)
		goto ErrEXIT;
	for (i = 1; i < pl; i++)
		working[i] = working[i - 1] + w;
	if (newinf->bits < 8) {
		w = newinf->xbytes * newinf->height;
		if ((planes[0] = (unsigned char *)malloc(w * pl)) == NULL)
			goto ErrEXIT;
		for (i = 1; i < pl; i++)
			planes[i] = planes[i - 1] + w;
	}

	tw = [[ToyWin alloc] init: self];
	[tw initLocateWindow:fn
		Width:newinf->width Height:newinf->height Num:NewLoc];

	if (angle == 45) {
		int yorg = (cinf->width + 1) >> 1;
		int xt, yt;
		for (y = 0; y < cinf->height; y++) {
		    for (x = 0; x < cinf->width; x++) {
			getPixelA(pix);
			if (((xt = x + y) & 1) == 0) {
				xt >>= 1;
				yt = yorg + ((y - x) >> 1);
				ptr = yt * newinf->width;
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + xt] = pix[i];
				}
			}
		    }
		}
	}else if (angle == 90) {
		for (x = 0; x < newinf->width; x++) {
			for (y = newinf->height - 1; y >= 0; y--) {
				ptr = y * newinf->width;
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
		}
	}else if (angle == 270) {
		for (x = newinf->width - 1; x >= 0; x--) {
			for (y = 0; y < newinf->height; y++) {
				ptr = y * newinf->width;
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
		}
	}else if (angle == 180) {
		for (y = newinf->height - 1; y >= 0; y--) {
			ptr = y * newinf->width;
			for (x = newinf->width - 1; x >= 0; x--) {
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
		}
	}else if (angle == Horizontal) {
		for (y = 0; y < newinf->height; y++) {
			ptr = y * newinf->width;
			for (x = newinf->width - 1; x >= 0; x--) {
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
		}
	}else if (angle == Vertical) {
		for (y = newinf->height - 1; y >= 0; y--) {
			ptr = y * newinf->width;
			for (x = 0; x < newinf->width; x++) {
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
		}
	}else if (angle == Clip) {
		int skipx = select->origin.x;
		int skipt = cinf->width - (skipx + select->size.width);
		int skipy = cinf->height
			- (select->origin.y + select->size.height);
		for (y = 0; y < skipy; y++) {
			for (x = 0; x < cinf->width; x++)
				getPixelA(pix);
		}
		for (y = 0; y < newinf->height; y++) {
			ptr = y * newinf->width;
			for (x = 0; x < skipx; x++)
				getPixelA(pix);
			for (x = 0; x < newinf->width; x++) {
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = pix[i];
				}
			}
			for (x = 0; x < skipt; x++)
				getPixelA(pix);
		}
	}else /* Negative */ {
		for (y = 0; y < newinf->height; y++) {
			ptr = y * newinf->width;
			for (x = 0; x < newinf->width; x++) {
				getPixelA(pix);
				for (i = 0; i <= ALPHA; i++) {
					if ((pidx = idx[i]) < 0) continue;
					working[pidx][ptr + x] = 0xff - pix[i];
				}
			}
		}
		if (newinf->palette) {
			unsigned char *p;
			paltype *pal = newinf->palette;
			for (x = 0; x < newinf->palsteps; x++) {
				p = pal[x];
				for (i = 0; i < 3; i++)
					p[i] = 0xff - p[i];
			}
		}
	}

	sprintf(newinf->memo, "%d x %d  %dbit%s",
		newinf->width, newinf->height, newinf->bits,
		((newinf->bits > 1) ? "s" : ""));
	if (newinf->bits < 8) {
		packWorkingImage(newinf, pl, working, planes);
		if ([tw drawView:planes info: newinf] == nil)
			goto ErrEXIT;
		free((void *)working[0]);
	}else {
		if ([tw drawView:working info: newinf] == nil)
			goto ErrEXIT;
	}
	[self newWindow: tw];
	return self;

ErrEXIT:
	if (working[0]) free((void *)working[0]);
	if (planes[0]) free((void *)planes[0]);
	if (newinf) {
		if (newinf->palette) free((void *)newinf->palette);
		free((void *)newinf);
	}
	if (tw) [tw free];
	return nil;
}


- doRotateFlipClip: (int)angle
{
	ToyWin	*tw;
	ToyView	*tv = NULL;
	NXImageRep	*rep;
	commonInfo	*cinf;
	NXRect		*select = NULL, rct;
	unsigned char	*map[5];
	char	*ex = NULL, fn[256];
	float	sf;
	int	err;

	if ((tw = [self keyWindow]) != nil) {
	    tv = [tw toyView];
	    if (angle == Clip) {
		select = [tv selectedRect];
		sf = [tv scaleFactor];
		if (sf != 1.0) {
		    rct.size.width = (int)(select->size.width / sf + 0.5);
		    rct.size.height = (int)(select->size.height / sf + 0.5);
		    rct.origin.x = (int)(select->origin.x / sf + 0.5);
		    rct.origin.y = (int)(select->origin.y / sf + 0.5);
		    select = &rct;
		}
		if (select->size.width < 1.0 || select->size.height < 1.0)
		    tw = nil;
	    }
	}
	if (tw == nil) {
		NXBeep();
		return self;
	}

	cinf = [tv commonInfo];
	if (cinf->width >= MAXWidth || cinf->height >= MAXWidth
	|| (angle == 45 && Width45(cinf->width, cinf->height) >= MAXWidth)) {
		errAlert([tw filename], Err_MEMORY);
		return self;
	}
	if (angle > 0) ex = "Rotate";
	else if (angle == Clip) ex = "Clip";
	else if (angle == Negative) ex = "Negative";
	else ex = "Flip";
	sprintf(fn, "%s(%s)", [tw filename], ex);

	if (cinf->type == Type_eps) {
		NXStream	*stream;
		ToyWinEPS	*newtw;

		if (angle == Negative) {
			errAlert([tw filename], Err_EPS_IMPL);
			return self;
		}
		stream = (angle == Clip)
			? [(ToyWinEPS *)tw clipEPS:select error:&err]
			: [(ToyWinEPS *)tw rotateEPS:angle name:fn error:&err];
		if (stream == NULL) {
			errAlert(fn, err);
			return self;
		}
		newtw = [[ToyWinEPS alloc] init: self];
		err = [newtw drawFromFile:(const char *)fn or:stream Num:NewLoc];
		NXCloseMemory(stream, NX_FREEBUFFER);
		if (err) {
			errAlert(fn, err);
			[newtw free];
		}else
			[self newWindow: newtw];
		return self;
	}

	if (cinf->cspace == NX_CMYKColorSpace) {
		errAlert(fn, Err_IMPLEMENT);
		return self;
	}

	rep = [[tv image] bestRepresentation];
	[(NXBitmapImageRep *)rep getDataPlanes: map];
	if ((err = initGetPixel(cinf)) != 0) {
		errAlert(fn, err);
		return self;
	}
	resetPixel(map, 0);
	if ([self doBitmap:fn info:cinf to:angle rect:select] == nil)
		errAlert(fn, Err_MEMORY);

	return self;
}

- rotate: sender
{
	static int tagAngles[] = { 45, 90, 270, 180 };
	return [self doRotateFlipClip: tagAngles[[sender selectedTag]]];
}

- flip: sender
{
	static int flipkind[]
		= { Horizontal, Vertical, Negative, Clip };
	return [self doRotateFlipClip: flipkind[[sender selectedTag]]];
}

@end
